
#include "StdAfx.h"
#include ".\volumecontrol.h"

// 		Defines
#define	BAD_DWORD	(DWORD)-1
#define	WND_CLASS_NAME	_T("Master Output Volume Msg Wnd Class")
#define	WND_NAME		_T("Master Output Volume Msg Wnd")

/////////////////////////////////////////////////////////////////////////////
// 		Globals

CVolumeControl::CVolumeControl(DWORD nVolumeType)
	: m_nVolumeType(nVolumeType),
		m_bOK(false),
		m_bInitialized(false),
		m_bAvailable(false),

		m_pMixer(NULL),
		m_hMixerHandle(NULL),

		m_dwMixerLineType(0),
		m_dwVolumeControlID(0),
		m_dwMuteControlID(0),

		m_dwMinimalVolume(BAD_DWORD),
		m_dwMaximalVolume(BAD_DWORD),

		m_pfUserSink(NULL),
		m_dwUserValue(0L)
{
	switch(m_nVolumeType)
	{
	case VOLUME_SPEAKER:
		m_uMixerID = 0;
		m_dwMixerLineType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
		break;
	case VOLUME_MICROPHONE:
		m_uMixerID = 0;
		m_dwMixerLineType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
		break;
	case VOLUME_REC_MICROPHONE:
		m_uMixerID = 1;
		m_dwMixerLineType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
		break;
	default:
		m_dwMixerLineType = 0;
	}

	if ( m_bOK = Init() )
	{
		if ( !Initialize() )
		{
			Done();
		}
	}
}

CVolumeControl::~CVolumeControl(void)
{
	if ( m_bOK )
		Done();
}

bool CVolumeControl::Init()
{
	m_pMixer = CMixer::OpenMixer(m_uMixerID);
	if(m_pMixer == NULL)
	{
		return false;
	}

	m_hMixerHandle = m_pMixer->GetHandle();
	m_pMixer->AddEventReceiver(this);

	return true;
}

void CVolumeControl::Done()
{
	if(m_pMixer)
	{
		m_pMixer->CloseMixer();
		m_hMixerHandle = (HMIXER)-1L;
		m_pMixer = NULL;
	}

	m_bInitialized = false;
	m_bAvailable = false;
	m_bOK = false;
}

bool CVolumeControl::Initialize()
{
	if ( !m_bOK )
		return false;

	MMRESULT mmResult;
	MIXERLINE MixerLine = {0};

	MixerLine.cbStruct = sizeof(MIXERLINE);
	MixerLine.dwComponentType = m_dwMixerLineType;

	mmResult = mixerGetLineInfo((HMIXEROBJ)m_hMixerHandle, &MixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE );
	if ( mmResult != MMSYSERR_NOERROR )
	{
		return false;
	}

	if(m_nVolumeType == VOLUME_REC_MICROPHONE)
	{
		bool bOK = false;
		DWORD dwConnections = MixerLine.cConnections;
		for(DWORD i = 0; i < dwConnections; i++)
		{
			MixerLine.dwSource = i;
			mmResult = mixerGetLineInfo((HMIXEROBJ)m_hMixerHandle, &MixerLine, MIXER_GETLINEINFOF_SOURCE);
			if(mmResult != MMSYSERR_NOERROR)
			{
				break;
			}

			if(MixerLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)
			{
				bOK = true;
				break;
			}
		}

		if(!bOK)
			return false;
	}

	MIXERCONTROL Control = {0};
	Control.cbStruct = sizeof(MIXERCONTROL);

	MIXERLINECONTROLS LineControls = {0};
	LineControls.cbStruct = sizeof(MIXERLINECONTROLS);

	LineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
	LineControls.dwLineID = MixerLine.dwLineID;
	LineControls.cControls = 1;
	LineControls.cbmxctrl = sizeof(MIXERCONTROL);
	LineControls.pamxctrl = &Control;

	mmResult = mixerGetLineControls((HMIXEROBJ)m_hMixerHandle, &LineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE );
	if ( mmResult == MMSYSERR_NOERROR )
	{
		if ( !(Control.fdwControl & MIXERCONTROL_CONTROLF_DISABLED) )
		{
			m_bAvailable = true;
		}
	}

	m_nChannelCount = MixerLine.cChannels;
	m_dwLineID = MixerLine.dwLineID;
	m_dwVolumeControlID = Control.dwControlID;
	m_dwMinimalVolume = Control.Bounds.dwMinimum;
	m_dwMaximalVolume = Control.Bounds.dwMaximum;
	m_dwVolumeStep = Control.Metrics.cSteps;

	LineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;

	mmResult = mixerGetLineControls((HMIXEROBJ)m_hMixerHandle, &LineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE );
	if( mmResult == MMSYSERR_NOERROR )
	{
		m_dwMuteControlID = Control.dwControlID;
	}

	m_bInitialized = true;
	return true;
}

void CVolumeControl::SetMute( bool bMute )
{
	if ( !m_bInitialized || m_dwMuteControlID == 0 )
		return;

	MIXERCONTROLDETAILS ControlDetails= {0};
	MIXERCONTROLDETAILS_BOOLEAN ControlDetailsValue;

	ControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
	ControlDetails.cChannels = 1;
	ControlDetails.cMultipleItems = 0;
	ControlDetails.dwControlID = m_dwMuteControlID;
	ControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
	ControlDetails.paDetails = &ControlDetailsValue;

	ControlDetailsValue.fValue = bMute;
	
	mixerSetControlDetails((HMIXEROBJ)m_hMixerHandle, &ControlDetails, MIXER_SETCONTROLDETAILSF_VALUE);
}

bool CVolumeControl::GetMute()
{
	if ( !m_bInitialized || m_dwMuteControlID == 0 )
		return false;

	MMRESULT mmResult;

	MIXERCONTROLDETAILS ControlDetails = {0};
	MIXERCONTROLDETAILS_BOOLEAN ControlDetailsValue;

	ControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
	ControlDetails.cChannels = 1;
	ControlDetails.cMultipleItems = 0;
	ControlDetails.dwControlID = m_dwMuteControlID;
	ControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
	ControlDetails.paDetails = &ControlDetailsValue;
	
	mmResult = mixerGetControlDetails((HMIXEROBJ)m_hMixerHandle, &ControlDetails, MIXER_GETCONTROLDETAILSF_VALUE);

	if(mmResult == MMSYSERR_NOERROR)
	{
		return (ControlDetailsValue.fValue != 0);
	}
	else
	{
		return false;
	}
}

DWORD CVolumeControl::GetVolumeMetric()
{
	if ( !m_bAvailable )
		return BAD_DWORD;
	return m_dwVolumeStep;
}

DWORD CVolumeControl::GetMinimalVolume()
{
	if ( !m_bAvailable )
		return BAD_DWORD;
	return m_dwMinimalVolume;
}

DWORD CVolumeControl::GetMaximalVolume()
{
	if ( !m_bAvailable )
		return BAD_DWORD;
	return m_dwMaximalVolume;
}

DWORD CVolumeControl::GetCurrentVolume()
{
	if ( !m_bAvailable )
		return BAD_DWORD;
	MIXERCONTROLDETAILS_UNSIGNED aDetails[10];// = (MIXERCONTROLDETAILS_UNSIGNED*)malloc(m_nChannelCount*sizeof(MIXERCONTROLDETAILS_UNSIGNED));
	if ( !aDetails )
		return BAD_DWORD;
	MIXERCONTROLDETAILS ControlDetails;
	memset( &ControlDetails, 0, sizeof(MIXERCONTROLDETAILS) );
	ControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
	ControlDetails.dwControlID = m_dwVolumeControlID;
	ControlDetails.cChannels = m_nChannelCount;
	ControlDetails.cMultipleItems = 0;
	ControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
	ControlDetails.paDetails = &aDetails[0];
	MMRESULT mmResult = mixerGetControlDetails( (HMIXEROBJ)m_hMixerHandle, &ControlDetails, MIXER_GETCONTROLDETAILSF_VALUE );
	DWORD dw = aDetails[0].dwValue;

	if ( mmResult != MMSYSERR_NOERROR )
	{
		return BAD_DWORD;
	}
	return dw;
}

void CVolumeControl::SetCurrentVolume( DWORD dwValue )
{
	if ( !m_bAvailable || (dwValue<m_dwMinimalVolume) || (dwValue>m_dwMaximalVolume) )
		return;
	MIXERCONTROLDETAILS_UNSIGNED aDetails[10];// = (MIXERCONTROLDETAILS_UNSIGNED*)malloc(m_nChannelCount*sizeof(MIXERCONTROLDETAILS_UNSIGNED));
	if ( !aDetails )
		return;
	for ( int i = 0; i < m_nChannelCount; i++ )
	{
		aDetails[i].dwValue = dwValue;
	}
	MIXERCONTROLDETAILS ControlDetails;
	memset( &ControlDetails, 0, sizeof(MIXERCONTROLDETAILS) );
	ControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
	ControlDetails.dwControlID = m_dwVolumeControlID;
	ControlDetails.cChannels = m_nChannelCount;
	ControlDetails.cMultipleItems = 0;
	ControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
	ControlDetails.paDetails = &aDetails[0];
	MMRESULT mmResult = mixerSetControlDetails( (HMIXEROBJ)m_hMixerHandle, &ControlDetails, MIXER_SETCONTROLDETAILSF_VALUE );
}

void CVolumeControl::RegisterNotificationSink( PONMICVOULUMECHANGE pfUserSink, DWORD dwUserValue )
{
	m_pfUserSink = pfUserSink;
	m_dwUserValue = dwUserValue;
}

void CVolumeControl::OnControlChanged(CMixer *pMixer, DWORD dwControlID)
{
	if(m_pfUserSink)
	{
		if ( m_dwVolumeControlID == dwControlID )
		{
			(*m_pfUserSink)(this, m_nVolumeType, CHANGE_VOLUME, m_dwUserValue );
		}
		else if(m_dwMuteControlID == dwControlID)
		{
			(*m_pfUserSink)(this, m_nVolumeType, CHANGE_MUTE_STATE, m_dwUserValue );
		}
	}
}
